package cn.iocoder.yudao.module.scenic.service.spot;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.scenic.constant.BaseConstant;
import cn.iocoder.yudao.module.scenic.controller.admin.spot.vo.*;
import cn.iocoder.yudao.module.scenic.controller.app.spot.vo.AppSpotNearbyReqVO;
import cn.iocoder.yudao.module.scenic.controller.app.spot.vo.SpotGatherRespVO;
import cn.iocoder.yudao.module.scenic.dal.dataobject.collect.CollectDO;
import cn.iocoder.yudao.module.scenic.dal.dataobject.spot.SpotDO;
import cn.iocoder.yudao.module.scenic.dal.dataobject.spot.SpotInfo;
import cn.iocoder.yudao.module.scenic.dal.mysql.collect.CollectMapper;
import cn.iocoder.yudao.module.scenic.dal.mysql.spot.SpotMapper;
import cn.iocoder.yudao.module.scenic.service.geo.GeoComponent;
import cn.iocoder.yudao.module.scenic.service.geo.vo.GeoVo;
import cn.iocoder.yudao.module.scenic.service.spot.SpotService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.scenic.enums.ErrorCodeConstants.SPOT_NOT_EXISTS;

/**
 * 实践点 Service 实现类
 *
 * @author 芋道源码
 */
@Slf4j
@Service
@Validated
public class SpotServiceImpl implements SpotService {

    @Resource
    private SpotMapper spotMapper;

    @Resource
    private GeoComponent geoComponent;
    @Resource
    private DeptApi deptApi;

    @Resource
    CollectMapper collectMapper;


    @Override
    public Long createSpot(SpotSaveReqVO createReqVO) {
        // 插入
        SpotDO spot = BeanUtils.toBean(createReqVO, SpotDO.class);
        spot.setIsOpen(true);
        spot.setDeptId(createReqVO.getDistrictId());
        spotMapper.insert(spot);
        // 记录经纬度 入库到redis中
        if (spot.getLnt() != null && spot.getLat() != null) {
            geoComponent.geoAdd(BaseConstant.SPOT_LOCATION, Double.parseDouble(spot.getLnt()), Double.parseDouble(spot.getLat()), String.valueOf(spot.getId()));
        }
        // 返回
        return spot.getId();
    }

    @Override
    public void updateSpot(SpotSaveReqVO updateReqVO) {
        // 校验存在
        validateSpotExists(updateReqVO.getId());
        // 更新
        SpotDO updateObj = BeanUtils.toBean(updateReqVO, SpotDO.class);
        spotMapper.updateById(updateObj);
    }

    @Override
    public void deleteSpot(Long id) {
        // 校验存在
        validateSpotExists(id);
        // 删除
        spotMapper.deleteById(id);
    }

    private void validateSpotExists(Long id) {
        if (spotMapper.selectById(id) == null) {
            throw exception(SPOT_NOT_EXISTS);
        }
    }

    @Override
    public SpotDO getSpot(Long id) {
        return spotMapper.selectById(id);
    }

    @Override
    public PageResult<SpotDO> getSpotPage(SpotPageReqVO pageReqVO) {
        return spotMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<SpotRespVO> getSpotPageByLocation(SpotPageByLocationReqVO pageReqVO) {
        QueryWrapper<SpotDO> queryWrapper = new QueryWrapper();
        if(null != pageReqVO.getProvinceId()){
            queryWrapper.eq("province_id",pageReqVO.getProvinceId());
        }
        if(null != pageReqVO.getCityId()){
            queryWrapper.eq("city_id",pageReqVO.getCityId());
        }
        if(null != pageReqVO.getDistrictId()){
            queryWrapper.eq("district_id",pageReqVO.getDistrictId());
        }
        if(StringUtil.isNotBlank(pageReqVO.getSpotName())){
            queryWrapper.like("name",pageReqVO.getSpotName());
        }
        queryWrapper.eq("deleted",false);
        PageResult<SpotDO> spotDOPageResult = spotMapper.selectPage(pageReqVO, queryWrapper);
        PageResult<SpotRespVO> respVOPageResult= new PageResult<>();
        List<SpotDO> list = spotDOPageResult.getList();
        if(!CollectionUtils.isEmpty(list)){
            Set<Long> idSet = new HashSet<>();
            for (SpotDO spotDO : list) {
                idSet.add(spotDO.getProvinceId());
                idSet.add(spotDO.getCityId());
                idSet.add(spotDO.getDistrictId());
            }
            List<Long>  idList = new ArrayList<>(idSet);
            CommonResult<List<DeptRespDTO>> result = deptApi.getDeptList(idList);
            Map<Long, DeptRespDTO> deptRespDTOMap = new HashMap<>();
            if(result.isSuccess()){
                List<DeptRespDTO> deptRespDTOList = result.getData();
                if(!CollectionUtils.isEmpty(deptRespDTOList)){
                    deptRespDTOMap = deptRespDTOList.stream().collect(Collectors.toMap(DeptRespDTO::getId, Function.identity()));
                }
            }
            Map<Long, DeptRespDTO> finalDeptRespDTOMap = deptRespDTOMap;
            List<SpotRespVO> spotRespVOList = list.stream().map(s -> {
                SpotRespVO spotInfo = new SpotRespVO();
                BeanUtil.copyProperties(s, spotInfo);
                Long provinceId = s.getProvinceId();
                Long cityId = s.getCityId();
                Long districtId = s.getDistrictId();
                StringBuilder belongAddress = new StringBuilder();
                if (finalDeptRespDTOMap.containsKey(provinceId)) {
                    belongAddress.append(finalDeptRespDTOMap.get(provinceId).getName());
                }
                if (finalDeptRespDTOMap.containsKey(cityId)) {
                    belongAddress.append("-");
                    belongAddress.append(finalDeptRespDTOMap.get(cityId).getName());
                }
                if (finalDeptRespDTOMap.containsKey(districtId)) {
                    belongAddress.append("-");
                    belongAddress.append(finalDeptRespDTOMap.get(districtId).getName());
                }
                spotInfo.setBelongAddress(belongAddress.toString());
                return spotInfo;
            }).collect(Collectors.toList());
            respVOPageResult.setTotal(spotDOPageResult.getTotal());
            respVOPageResult.setList(spotRespVOList);
        }
        return respVOPageResult;
    }

    @Override
    public AppSpotRespVO getAppSpot(Long id, String openId) {
        AppSpotRespVO respVO = new AppSpotRespVO();
        SpotDO spotDO = spotMapper.selectById(id);
        QueryWrapper<CollectDO> wrapper = new QueryWrapper();
        wrapper.eq("spot_id",id);
        wrapper.eq("open_id",openId);
        BeanUtil.copyProperties(spotDO,respVO);
        List<CollectDO> collectDOList = collectMapper.selectList(wrapper);
        if(CollectionUtil.isNotEmpty(collectDOList)){
            respVO.setIsCollect(true);
        }else {
            respVO.setIsCollect(false);
        }
        return respVO;
    }


    @Override
    public CommonResult<List<SpotRespVO>> nearby(AppSpotNearbyReqVO spotNearbyReqVO) {
        // todo 查找附近，目前市高德地图
        QueryWrapper<SpotDO> wrapper = new QueryWrapper<>();
        wrapper.eq("deleted",false);
        wrapper.isNotNull("lnt");
        wrapper.isNotNull("lat");
        List<SpotDO> spotDOS = spotMapper.selectList(wrapper);
        geoComponent.boundGeoOps(BaseConstant.SPOT_LOCATION, spotDOS.stream().collect(Collectors.toMap(e -> String.valueOf(e.getId()), spotDO -> {
            if (spotDO.getLnt() != null && spotDO.getLat() != null) {
                return new Point(Double.parseDouble(spotDO.getLnt()), Double.parseDouble(spotDO.getLat()));
            }
            return null;
        })));
        List<GeoVo> radius = geoComponent.radius(BaseConstant.SPOT_LOCATION, Double.parseDouble(spotNearbyReqVO.getLnt()), Double.parseDouble(spotNearbyReqVO.getLat()), 5);
        List<String> collect = radius.stream().map(GeoVo::getKey).collect(Collectors.toList());
       if (collect.isEmpty()) {
            return CommonResult.success(Collections.emptyList());
        }
        List<SpotDO> spotDOList = spotMapper.selectBatchIds(collect);
        Set<Long> idSet = new HashSet<>();
        for (SpotDO spotDO : spotDOList) {
            idSet.add(spotDO.getProvinceId());
            idSet.add(spotDO.getCityId());
            idSet.add(spotDO.getDistrictId());
        }
        List<Long>  idList = new ArrayList<>(idSet);
        CommonResult<List<DeptRespDTO>> result = deptApi.getDeptList(idList);
        Map<Long, DeptRespDTO> deptRespDTOMap = new HashMap<>();
        if(result.isSuccess()){
            List<DeptRespDTO> deptRespDTOList = result.getData();
            if(!CollectionUtils.isEmpty(deptRespDTOList)){
                deptRespDTOMap = deptRespDTOList.stream().collect(Collectors.toMap(DeptRespDTO::getId, Function.identity()));
            }
        }
        Map<Long, DeptRespDTO> finalDeptRespDTOMap = deptRespDTOMap;
        List<SpotRespVO> spotRespVOList = spotDOList.stream().map(s -> {
            SpotRespVO spotInfo = new SpotRespVO();
            BeanUtil.copyProperties(s, spotInfo);
            Long provinceId = s.getProvinceId();
            Long cityId = s.getCityId();
            Long districtId = s.getDistrictId();
            if (finalDeptRespDTOMap.containsKey(provinceId)) {
                spotInfo.setProvinceName(finalDeptRespDTOMap.get(provinceId).getName());
            }
            if (finalDeptRespDTOMap.containsKey(cityId)) {
                spotInfo.setCityName(finalDeptRespDTOMap.get(cityId).getName());
            }
            if (finalDeptRespDTOMap.containsKey(districtId)) {
                spotInfo.setDistrictName(finalDeptRespDTOMap.get(districtId).getName());
            }
            return spotInfo;
        }).collect(Collectors.toList());
        return CommonResult.success(spotRespVOList);
    }

    @Override
    public List<SpotGatherRespVO> gather(String level, Long deptId) {
        // todo 按组织机构统计实践点数量
        //
        List<SpotGatherRespVO> list = new ArrayList<>();
        // 市级机构
        CommonResult<List<DeptRespDTO>> childDeptList = deptApi.getSubordinateDept(deptId);
        if (childDeptList.isError()) {
            return Collections.emptyList();
        }
        // 市机构列表
        List<DeptRespDTO> cityDeptDTOs = childDeptList.getCheckedData();
        Map<String, List<DeptRespDTO>> map = new HashMap<>();
        for (DeptRespDTO deptRespDTO : cityDeptDTOs) {
            CommonResult<List<DeptRespDTO>> deptList = deptApi.getChildDeptList(deptRespDTO.getId());
            map.put(deptRespDTO.getName(), deptList.getCheckedData());
            List<DeptRespDTO> deptRespDTOS = map.get(deptRespDTO.getName());
            deptRespDTOS.add(deptRespDTO);
            List<Long> collect = deptRespDTOS.stream().map(DeptRespDTO::getId).collect(Collectors.toList());
            List<SpotDO> spotDOS = spotMapper.selectList(SpotDO::getDeptId, collect);
            SpotGatherRespVO spotGatherRespVO = new SpotGatherRespVO();
            spotGatherRespVO.setDeptId(String.valueOf(deptRespDTO.getId()));
            spotGatherRespVO.setDeptName(deptRespDTO.getName());
            spotGatherRespVO.setSpotsNum(spotDOS.size());
            list.add(spotGatherRespVO);
        }

        return list;
    }

    @Override
    public List<SpotRespVO> listByDeptId(String deptId) {
        // 获取组织下所有的实践点
        QueryWrapper<SpotDO> queryWrapper = new QueryWrapper();
        queryWrapper.eq("dept_id",deptId).or().eq("province_id",deptId).or().eq("city_id",deptId)
        .or().eq("district_id",deptId);
        List<SpotDO> spotDOList = spotMapper.selectList(queryWrapper);
        if(CollectionUtils.isEmpty(spotDOList)){
            return new ArrayList<>();
        }
        Set<Long> idSet = new HashSet<>();
        for (SpotDO spotDO : spotDOList) {
            idSet.add(spotDO.getProvinceId());
            idSet.add(spotDO.getCityId());
            idSet.add(spotDO.getDistrictId());
        }
        log.info("查询的省市区id个数为：{}",idSet.size());
        List<Long>  idList = new ArrayList<>(idSet);
        CommonResult<List<DeptRespDTO>> result = deptApi.getDeptList(idList);
        Map<Long, DeptRespDTO> deptRespDTOMap = new HashMap<>();
        if(result.isSuccess()){
            List<DeptRespDTO> deptRespDTOList = result.getData();
            log.info("查询出的省市区个数为：{}",deptRespDTOList.size());
            //查询出省市区信息
            deptRespDTOList.stream().forEach(x -> {
                log.info("id为：{},名称为：{}",x.getId(),x.getName());
            });
            if(!CollectionUtils.isEmpty(deptRespDTOList)){
                deptRespDTOMap = deptRespDTOList.stream().collect(Collectors.toMap(DeptRespDTO::getId, Function.identity()));
            }
        }
        Map<Long, DeptRespDTO> finalDeptRespDTOMap = deptRespDTOMap;
        List<SpotRespVO> spotInfoList = spotDOList.stream().map(s -> {
            SpotRespVO vo = new SpotRespVO();
            BeanUtil.copyProperties(s, vo);
            Long provinceId = s.getProvinceId();
            Long cityId = s.getCityId();
            Long districtId = s.getDistrictId();
            if (finalDeptRespDTOMap.containsKey(provinceId)) {
                vo.setProvinceName(finalDeptRespDTOMap.get(provinceId).getName());
            }
            if (finalDeptRespDTOMap.containsKey(cityId)) {
                vo.setCityName(finalDeptRespDTOMap.get(cityId).getName());
            }
            if (finalDeptRespDTOMap.containsKey(districtId)) {
                vo.setDistrictName(finalDeptRespDTOMap.get(districtId).getName());
            }
            return vo;
        }).collect(Collectors.toList());
        return spotInfoList;
    }

    @Override
    public List<SpotRespVO> listByName(String name) {
        log.info("查询实践点，当前名称为：{}",name);
        // 获取实践点名称包含name的实践点
        List<SpotDO> spotDOList = spotMapper.selectList(new LambdaQueryWrapper<SpotDO>().like(SpotDO::getName, name));
        if(CollectionUtils.isEmpty(spotDOList)){
            return new ArrayList<>();
        }
        Set<Long> idSet = new HashSet<>();
        for (SpotDO spotDO : spotDOList) {
            idSet.add(spotDO.getProvinceId());
            idSet.add(spotDO.getCityId());
            idSet.add(spotDO.getDistrictId());
        }
        log.info("查询的省市区id个数为：{}",idSet.size());
        List<Long>  idList = new ArrayList<>(idSet);
        CommonResult<List<DeptRespDTO>> result = deptApi.getDeptList(idList);
        Map<Long, DeptRespDTO> deptRespDTOMap = new HashMap<>();
        if(result.isSuccess()){
            List<DeptRespDTO> deptRespDTOList = result.getData();
            log.info("查询出的省市区个数为：{}",deptRespDTOList.size());
            //查询出省市区信息
            deptRespDTOList.stream().forEach(x -> {
                log.info("id为：{},名称为：{}",x.getId(),x.getName());
            });
            if(!CollectionUtils.isEmpty(deptRespDTOList)){
                deptRespDTOMap = deptRespDTOList.stream().collect(Collectors.toMap(DeptRespDTO::getId, Function.identity()));
            }
        }
        Map<Long, DeptRespDTO> finalDeptRespDTOMap = deptRespDTOMap;
        List<SpotRespVO> spotInfoList = spotDOList.stream().map(s -> {
            SpotRespVO vo = new SpotRespVO();
            BeanUtil.copyProperties(s, vo);
            Long provinceId = s.getProvinceId();
            Long cityId = s.getCityId();
            Long districtId = s.getDistrictId();
            if (finalDeptRespDTOMap.containsKey(provinceId)) {
                vo.setProvinceName(finalDeptRespDTOMap.get(provinceId).getName());
            }
            if (finalDeptRespDTOMap.containsKey(cityId)) {
                vo.setCityName(finalDeptRespDTOMap.get(cityId).getName());
            }
            if (finalDeptRespDTOMap.containsKey(districtId)) {
                vo.setDistrictName(finalDeptRespDTOMap.get(districtId).getName());
            }
            return vo;
        }).collect(Collectors.toList());
        return spotInfoList;
    }



}